#include "ODN_GameMgr.h"

//#include <TinyXML\TinyXML.h>

#include "ODN_GameState.h"
#include "ODN_OgreInputDevice.h"

namespace ouden {

template<>
CGameMgr* CSingleton<CGameMgr>::m_pInstance = 0;

CGameMgr::CGameMgr()
{
	//ctor

	//We're only just getting started
	m_bFinished = false;

	//Don't start in a networked mode
	m_bIsClient = false;
	m_bIsServer = false;

	//NULL the uninitialised pointers for safety
	m_pRoot = 0;
	m_pSceneMgr = 0;
	m_pEventProcessor = 0;
	m_pInputDevice = 0;
	m_pGameTimer = 0;
	m_pGameWorld = 0;
	m_pClient = 0;
	m_pServer = 0;


}

CGameMgr::~CGameMgr()
{

	std::cout << std::endl << "!!!!!!!!!!!!!!!!!!!CGameMgr destructor" << std::endl << std::endl;

	//dtor
}

//Main engine initialisation
bool CGameMgr::init()
{

	//Create the root Ogre object
	m_pRoot = new Ogre::Root();

	// Show the configuration dialog and initialise the system
	// You can skip this and use root.restoreConfig() to load configuration
	// settings if you were sure there are valid ones saved in ogre.cfg
	if(m_pRoot->showConfigDialog())
	{
		// If returned true, user clicked OK so initialise
		// Here we choose to let the system create a default rendering window by passing 'true'
		m_pWindow = m_pRoot->initialise(true, "Ouden Engine Game");
	}
	else
	{
		return false;
	}

	//Create the Ogre scene manager
	m_pSceneMgr = m_pRoot->getSceneManager(Ogre::ST_GENERIC);

	//Load all the game resources
	loadResources();

	// Initialise, parse scripts etc
	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

	//TODO: Loading screen as mentioned in ExampleApplication setup resource listener

	//Let Ogre know that this is a frame listener
	m_pRoot->addFrameListener(this);

	//////////////////////
	//Setup input handling
	//////////////////////
	//Create and initialise a new event processor
	m_pEventProcessor = new Ogre::EventProcessor();
	m_pEventProcessor->initialise(m_pWindow);
	m_pEventProcessor->startProcessingEvents();
	//Register this class as a keylistener
	m_pEventProcessor->addKeyListener(this);
	//Get a pointer to an input reader device
	//m_pInputReader = m_pEventProcessor->getInputReader();

	//Create an input device
	//TODO: Check where this input device should be decided
	m_pInputDevice = new COgreInputDevice(m_pEventProcessor);

	///////////////////////
	//Setup the main camera
	///////////////////////
	// Create the camera
	m_pCamera = m_pSceneMgr->createCamera("MainCam");
	// Position it at the origin.
	m_pCamera->setPosition(Ogre::Vector3(0, 0, 0.5));
	// Look back along -Z
	m_pCamera->lookAt(Ogre::Vector3(0,0,-300));
	m_pCamera->setNearClipDistance(0.1);

	//Get a viewport
	m_pViewport = m_pRoot->getAutoCreatedWindow()->addViewport(m_pCamera);
	m_pViewport->setBackgroundColour(Ogre::ColourValue(0.0, 1.0, 0.0));
	// Alter the camera aspect ratio to match the viewport
	m_pCamera->setAspectRatio(Ogre::Real(m_pViewport->getActualWidth()) / Ogre::Real(m_pViewport->getActualHeight()));

	//Create a game timer
	m_pGameTimer = new CGameTimer();

	//Create the opal simulator
	m_pSimulator = opal::createSimulator();
	m_pSimulator->setGravity(opal::Vec3r(0, (opal::real)-9.81, 0));

	//Create an instance of a game world
	m_pGameWorld = new ouden::CGameWorld();


	assert(m_pRoot);
	assert(m_pEventProcessor);
	assert(m_pInputDevice);
	assert(m_pGameTimer);
	assert(m_pGameWorld);

    // Register core game entities TODO: check if we should
    REGISTER_ENTITY_CLASS("CModelEntity", ouden::CModelEntity);


	std::cout << "End of init" << std::endl;
	return true;

}

//Main engine cleanup when everything's finished
bool CGameMgr::cleanup()
{

	//Exit all the states
	while (!m_pGameStates.empty()) {
		m_pGameStates.back()->exit();
		m_pGameStates.pop_back();
	}

	//Clean up ogre stuff
	m_pSceneMgr->clearScene();
	//!! Note: This is supposed to be mSceneMgr->destroyAllCameras(); for CVS head
	m_pSceneMgr->removeAllCameras();
	m_pRoot->getAutoCreatedWindow()->removeAllViewports();

	//Delete the dynamically allocated memory if required
	if (m_pClient)
		delete m_pClient;
	if (m_pServer)
		delete m_pServer;

	if (m_pGameWorld)
		delete m_pGameWorld;
	if (m_pGameTimer)
		delete m_pGameTimer;
	if (m_pInputDevice)
		delete m_pInputDevice;
	if (m_pEventProcessor)
		delete m_pEventProcessor;
	if (m_pRoot)
		delete m_pRoot;

}

bool CGameMgr::loadResources()
{

	// Load resource paths from config file
	Ogre::ConfigFile cf;
	cf.load("resources.cfg");

	// Go through all sections & settings in the file
	Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

	String secName, typeName, archName;
	while (seci.hasMoreElements())
	{
		secName = seci.peekNextKey();
		Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
		Ogre::ConfigFile::SettingsMultiMap::iterator i;
		for (i = settings->begin(); i != settings->end(); ++i)
		{
			typeName = i->first;
			archName = i->second;
			Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
				archName, typeName, secName);
		}
	}

	return true;

}

//Starts the engine running
void CGameMgr::start(CGameState* pGameState)
{

	assert(pGameState);

	//Initialise the engine
	if (!init())
		return;

	//Set the initial game state
	changeState(pGameState);

	//Begin the game
	//NOTE This only returns when a frameStarted or frameEnded event returns false
	m_pRoot->startRendering();

	//Clean up the engine before the program ends
	cleanup();

}

bool CGameMgr::frameStarted(const Ogre::FrameEvent &evt)
{

	CGameState* pCurGameState = m_pGameStates.back();

	m_pInputDevice->captureInput();

	//Let the current game state process unbuffered input
	pCurGameState->onUnBufInput(m_pInputDevice);

	if (m_bIsClient)
		m_pClient->processPackets();
	if (m_bIsServer)
		m_pServer->processPackets();

	//if (m_pNetworkMgr)
	//	m_pNetworkMgr->processPackets();

	//Update the timer and call the tick and frame started events for the current game state
	return m_pGameTimer->update(pCurGameState);

	/*
	std::cout << "Frame Start" << std::endl;
	//Check for escape key exit
	if(m_pInputReader->isKeyDown(Ogre::KC_ESCAPE))
		return false;

	//Capture input
	m_pInputReader->capture();

	//Pass input to the game world
	m_pGameWorld->processInput(m_pInputReader);

	//Update the gameworld
	m_pGameWorld->update();

	return true;
	*/
}

bool CGameMgr::frameEnded(const Ogre::FrameEvent &evt)
{

	return m_pGameStates.back()->frameEnded( );

	/*
	if (m_bFinished)
		return false;

	return true;
	*/
}

//Buffered key events get passed to the current game state class
void CGameMgr::keyPressed(Ogre::KeyEvent *e)
{

	m_pGameStates.back()->onKeyDown(e->getKey());

}
void CGameMgr::keyReleased(Ogre::KeyEvent *e)
{

	m_pGameStates.back()->onKeyUp(e->getKey());

}
void CGameMgr::keyClicked(Ogre::KeyEvent *e)
{

	m_pGameStates.back()->onKeyPress(e->getKey());

}

void CGameMgr::changeState(CGameState* state)
{

	// cleanup the current state
	if ( !m_pGameStates.empty() ) {
		m_pGameStates.back()->exit();
		m_pGameStates.pop_back();
	}

	// store and init the new state
	m_pGameStates.push_back(state);
	m_pGameStates.back()->enter();
}

void CGameMgr::pushState(CGameState* state)
{
	// pause current state
	if ( !m_pGameStates.empty() ) {
		m_pGameStates.back()->pause();
	}

	// store and init the new state
	m_pGameStates.push_back(state);
	m_pGameStates.back()->enter();
}

void CGameMgr::popState()
{
	// cleanup the current state
	if ( !m_pGameStates.empty() ) {
		m_pGameStates.back()->exit();
		m_pGameStates.pop_back();
	}

	// resume previous state
	if ( !m_pGameStates.empty() ) {
		m_pGameStates.back()->resume();
	}
}
/*
void CGameMgr::createModelEntity(String name, String modelFile, Vector pos, Angle orientation)
{
	std::cout << "1 " << m_pSceneMgr << std::endl;
	//Load the mesh into an Ogre entity
	Ogre::Entity* ogreEntity = m_pSceneMgr->createEntity(name, modelFile + ".mesh");
	ogreEntity->setMaterialName("Plastic/Red");
	// Keep the normals normalized even after scaling.
	ogreEntity->setNormaliseNormals(true);
	std::cout << "2" << std::endl;
	//Load physics blueprint from file
	opal::Blueprint blueprint;
	opal::loadFile(blueprint, MODEL_PATH + modelFile + " blueprint.xml");
	opal::Matrix44r transform;
	transform.translate(pos[0], pos[1], pos[2]);
	//Instantiate the Blueprint.
	opal::BlueprintInstance blueprintInstance;
	m_pSimulator->instantiateBlueprint(blueprintInstance, blueprint, transform, 16);
	std::cout << "3" << std::endl;
	//Create the new model entity
	//TODO: Constructor parameters
	CModelEntity* newModelEntity = new CModelEntity(name);
	std::cout << "4" << std::endl;
	//Now we must load our own special model file in order to link the solids from the blueprint
	// to the bones in the skeleton

	//Open our model file
	TiXmlDocument doc( (MODEL_PATH + modelFile + " model.xml").c_str() );
	doc.LoadFile();
	//TODO: Error checking
	//Create a document handle
	TiXmlHandle docHandle( &doc );
	//Get the model data
	TiXmlElement* nodeModel = docHandle.FirstChild( "Model" ).Element();

	std::cout << "solids " << blueprintInstance.getNumSolids() << std::endl;

	//Loop through each solid
	for (int i=0; i<blueprintInstance.getNumSolids(); ++i) {

		//Get a pointer to the solid
		opal::Solid* opalSolid = blueprintInstance.getSolid(i);

		//Check if the mesh has a skeleton
		//TODO: WARNINGNESS This should maybe be outside the loop so the loop is inside the if
		// Can you have more than one solid but no skeleton?
		if (ogreEntity->hasSkeleton()) {

			//If so then we need to attach each solid to its proper bone according to our model file data

			//Get the first solid to bone assignment
			TiXmlElement* nodeAssignment = nodeModel->FirstChildElement( "SolidToBoneAssignment" );

			//Read the index of the solid
			int solidIndex;
			nodeAssignment->QueryIntAttribute( "solidindex" , &solidIndex);

			//Read the name of the bone
			String boneName = nodeAssignment->Attribute( "bonename" );

			//Get pointer to the bone from the mesh skeleton
			Ogre::Node* boneNode = ogreEntity->getSkeleton()->getBone(boneName);

			//Create a new model bit, linking the solid to the bone
			CModelBit* newModelBit = new CModelBit(opalSolid, boneNode);

			//Add the new bit to the model
			newModelEntity->addBit(newModelBit);

		}
		else
		{

			//If no skeleton then just make a single scene node and attach the mesh to it

			//Create an Ogre SceneNode for the skeletonless model entity
			Ogre::SceneNode* ogreSceneNode = m_pSceneMgr->getRootSceneNode()->createChildSceneNode(name);

			//Attach the entity to the scene node
			ogreSceneNode->attachObject(ogreEntity);

			//Create a new model bit, linking the solid to the scene node
			CModelBit* newModelBit = new CModelBit(opalSolid, ogreSceneNode);

			//Add the new bit to the model
			newModelEntity->addBit(newModelBit);

		}

	}


}
*/
} //namespace ouden
